package org.opensourcephysics.display.axes;
import java.awt.Color;
import java.text.DecimalFormat;
import org.opensourcephysics.display.DrawingPanel;
import java.awt.Graphics;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;

/**
 * AbstractPolarAxis implements methods common to all polar axes.
 *
 * @author Wolfgang Christian
 * @version 1.0
 */
public abstract class AbstractPolarAxis extends AbstractAxes   implements PolarAxes{
  static final double LOG10 = Math.log(10);
  static int MAJOR_TIC=5;
  protected double dr = 1;
  protected double dtheta = Math.PI/8;
  protected Color interiorColor = Color.white;
  protected boolean autospaceRings=true;

  /**
   * Creates polar axes that will display themselves within the given drawing panel.
   *
   * @param drawingPanel DrawingPanel
   */
  protected AbstractPolarAxis(DrawingPanel drawingPanel){
     super(drawingPanel);
  }

  /**
   * Sets the interior background color.
   */
  public void setInteriorBackground(Color color){
     interiorColor = color;
  }


  /**
   * Automatically sets the spacing of the radial grid.
   * @param autoscaleR
   */
  public void autospaceRings(boolean autospace) {
    this.autospaceRings = autospace;
  }


  /**
   * Gets the spacing of the radial grid.
   */
  public double getDeltaR() {
    return dr;
  }

  /**
   * Sets the spacing of the radial gridlines.
   * @param dr
   */
  public void setDeltaR(double dr) {
    this.dr = dr;
  }

  /**
   * Gets the spacing of the radial gridlines.
   */
  public double getDeltaTheta() {
    return dtheta;
  }

  /**
   * Sets the spacing of the radial gridlines.
   * @param dtheta in degree
   */
  public void setDeltaTheta(double dtheta) {
    this.dtheta = Math.abs(dtheta);
  }


  /**
   * Method setLabelFormat
   *
   * @param formatString
   */
  public void setLabelFormat(String formatString){
     labelFormat = new DecimalFormat(formatString);
  }

  /**
   * Draws the spokes for the polar plot.
   * @param panel
   * @param g
   */
  protected void drawRAxis(double dr, double rmax, DrawingPanel panel, Graphics g){
     Graphics2D g2= (Graphics2D)g;
     g.setColor(gridcolor.darker());
     int x1 = panel.xToPix(0);
     int y1 = panel.yToPix(0);
     int x2 = panel.xToPix(rmax);
     g.drawLine(x1, y1, Math.min(x2,panel.getWidth()-panel.getRightGutter()), y1);
     FontMetrics fm = g2.getFontMetrics();
     int nLabels=(int)(panel.getXMax()/dr/MAJOR_TIC);
     int stride=(nLabels>3)?2:1;
     double rm=Math.min(rmax,panel.getXMax());
     for(double r=(nLabels>3)?stride*MAJOR_TIC*dr:MAJOR_TIC*dr; r<=rm; r+=(stride*MAJOR_TIC*dr)){
        String label = getLabel(r);
        int sW = fm.stringWidth(label)+4;
        int sH = fm.getHeight();
        g2.setColor(new Color(247, 247, 247));
        int x0 = panel.xToPix(r), y0 = panel.yToPix(0);
        g2.fill(new Rectangle2D.Double(x0-sW/2, y0+3, sW, sH));
        g2.setColor(Color.black);
        g2.draw(new Rectangle2D.Double(x0-sW/2, y0+3, sW, sH));
        g2.setColor(Color.BLACK);
        g2.drawString(label, x0-sW/2+2, y0+1+sH);
     }
  }

  String getLabel(double r){
     if(r>=10)return Integer.toString((int)r);
     return Double.toString(r);
  }

  /**
   * Draws the rings for the polar plot.
   * @param panel
   * @param g
   * 
   * @return double the ring separation used
   */
  public double drawRings(double rmax, DrawingPanel panel, Graphics g){
     double dr =Math.max( this.dr, 1.0e-9);
     if (autospaceRings){
        int exponent=(int)(Math.log(rmax)/LOG10);
        double decade=Math.pow(10,exponent-1);
        dr = decade;
        while (rmax/dr>5*MAJOR_TIC){ // increase dr if we have more than 25 rings
           dr *= 2;
           if (dr/decade>3.5&&dr/decade<4.5){
              dr = 5*decade;
              decade *= 10;
           }
        }
     } else{
        int nrings = (int) (rmax/dr);
        while (nrings>10*MAJOR_TIC){
           dr *= 2;
           nrings = (int) (rmax/dr);
        }
     }
     int xcenter = panel.xToPix(0);
     int ycenter = panel.yToPix(0);
     int xrad = (int) (panel.getXPixPerUnit()*rmax);
     int yrad = (int) (panel.getYPixPerUnit()*rmax);
     if (interiorColor!=null){
        g.setColor(interiorColor);
        g.fillOval(xcenter-xrad, ycenter-yrad, 2*xrad, 2*yrad);
     }
     int counter=0;
     for (double r = 0; r<=rmax; r += dr){
        g.setColor(gridcolor);
        xrad = panel.xToPix(r)-xcenter;
        yrad = ycenter-panel.yToPix(r);
        if(counter%MAJOR_TIC==0){
            g.setColor(gridcolor.darker());
        }
        g.drawOval(xcenter-xrad, ycenter-yrad, 2*xrad, 2*yrad);
        counter++;
     }
     return dr;
  }



  /**
   * Draws the spokes for the polar plot.
   * @param panel
   * @param g
   */
  public void drawSpokes(double rmax, DrawingPanel panel, Graphics g){
     g.setColor(gridcolor);
     for (double theta = 0; theta<Math.PI; theta += dtheta){
        int x1 = panel.xToPix(rmax*Math.cos(theta));
        int y1 = panel.yToPix(rmax*Math.sin(theta));
        int x2 = panel.xToPix(-rmax*Math.cos(theta));
        int y2 = panel.yToPix(-rmax*Math.sin(theta));
        g.drawLine(x1, y1, x2, y2);
     }
  }
}
